/*************************************************************************
	> File Name: nonblock.cpp
	> Author: 
	> Mail: 
	> Created Time: Wed 20 Dec 2017 06:56:29 PM PST
 ************************************************************************/
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>

#include <strings.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include <iterator>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>
using std::map;
using std::vector;
using std::unordered_map;

typedef struct epoll_event epollev;
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;

class Buffer;
class ScanInfo;

map<int,Buffer> readBuffers;
map<int,Buffer> writeBuffers;

int g_epollfd;

void sys_err()
{
    printf("%s\n",strerror(errno));
    exit(-1);
}

class Buffer
{
public:
    static const int reserve = 4;
    Buffer():buffer_(reserve,0),readIndex(reserve),writeIndex(reserve) {}

    inline int readableBytes() { return writeIndex-readIndex; }
    inline int writableBytes() { return buffer_.capacity() - writeIndex; }
    inline int passedBytes() { return readIndex-4; }

    void put(const char* str,int len)
    {
        if(passedBytes() > len+readableBytes())
        {
            int n = readableBytes();
            std::copy(buffer_.begin()+readIndex,buffer_.begin()+writeIndex,buffer_.begin()+reserve);
            readIndex = reserve;
            writeIndex = reserve+n;
            std::copy(str,str+len,buffer_.begin()+writeIndex);
            writeIndex += len;
        }
        else
        {
            std::copy(str,str+len,buffer_.begin()+writeIndex);
            writeIndex += len;
        }
        printf("readIndex:%d    writeIndex:%d\n",readIndex,writeIndex);
    }

    char* retriveAll()
    {
        int n = readableBytes();
        memcpy(&*buffer_.begin(),(char*)&n,sizeof(int));
        readIndex = writeIndex;

        int test;
        memcpy(&test,&*buffer_.begin(),4);
        return &*(buffer_.begin()+readIndex);
    }

private:
    vector<char> buffer_;
    int readIndex;
    int writeIndex;
};

int do_accept(int listenfd)
{
    sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    int fd = accept(listenfd,(sockaddr*)&cliaddr,&len);
    if(fd == -1)
    {
        printf("%s\n",strerror(errno));
        return -1;
    }

    int flags = fcntl(fd,F_GETFL,0);
    fcntl(fd,F_SETFL,flags | O_NONBLOCK);
    printf("connection from %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
    return fd;
}

void do_read(int fd)
{
    char buf[65536];
    int n = read(fd,buf,65535);
    if(n < 0)
    {
        if(errno == EINTR || errno == EWOULDBLOCK)
            return;

        printf("read socket:%d error! error reason:%s\n",fd,strerror(errno));
        return ;
    }
    else if(n == 0)
    {
        close(fd);
        printf("peer end closed.\n");
        epoll_ctl(g_epollfd,EPOLL_CTL_DEL,fd,NULL);
        return ;
    }
    else
    {
        buf[n] = 0;
        Buffer& buffer = readBuffers[fd];
        buffer.put(buf,strlen(buf));
    }
}


void* timerScanBuffer(void* arg)
{
    int interval = *(int*)arg;    
    while(true)
    {
        for(auto itr=readBuffers.begin();itr != readBuffers.end();++itr)
        {
            Buffer& buffer = itr->second;
            char* bufferInfo = buffer.retriveAll();
            int n;
            memcpy(&n,bufferInfo,4);
            printf("fd[%d] buffer len:%d\n",itr->first,n);
            char temp[65535];
            std::copy(bufferInfo+4,bufferInfo+n+4,temp);
            temp[n]=0;
            printf("fd[%d] buffer scan:%s\n",itr->first,temp);
        }

        sleep(5);
    }
}

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
        sys_err();

    sockaddr_in servAddr;
    bzero(&servAddr,sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(8888);
    servAddr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(sockfd,(struct sockaddr*)&servAddr,sizeof(servAddr));
    if(ret == -1)
        sys_err();

    ret = listen(sockfd,100);
    if(ret == -1)
        sys_err();

    int epollfd = epoll_create(100);
    g_epollfd = epollfd;
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&ev);

    pthread_t tid;
    int interval = 5;
    pthread_create(&tid,NULL,timerScanBuffer,&interval);

    struct epoll_event evs[100];
    while(true)
    {
        int nready = epoll_wait(epollfd,evs,100,-1);
        if(nready == -1)
        {
            if(errno == EINTR)
                continue;
            else
                sys_err();
        }

        printf("%d fd is ready to deal with\n",nready);
        for(int i=0;i<nready;++i)
        {
            epollev& ev = evs[i];
            if(ev.events & EPOLLIN)
            {
                if(ev.data.fd == sockfd)
                {
                    int fd = do_accept(sockfd);
                    if(fd != -1)
                    {
                        epollev ev;
                        ev.events = EPOLLIN;
                        ev.data.fd = fd;
                        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
                    }
                }
                else
                    do_read(ev.data.fd);
            }
            else if(ev.events & EPOLLERR)
            {
                printf("epoll fd:%d error, error info:%s\n",ev.data.fd,strerror(errno));   
            }
        }
    }
}
